package com.cxf.ws.util;

import javax.xml.namespace.QName;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

import com.ylink.framework.util.ClassUtil;
import com.ylink.framework.util.CollectionUtils;
import com.ylink.framework.util.log.Log;
import com.ylink.framework.util.log.LogFactory;

/**
 * WebService工具类
 * 
 * @author 陈兴吉
 * @date 2012-11-8
 */
public class WebServiceClientUtil {
	private static final Log log = LogFactory.getLog(WebServiceClientUtil.class);
	
	/**
	 * 调用webservice方法
	 * 
	 * @param endpoint
	 *            调用地址
	 * @param interfaceClass
	 *            webservice的接口
	 * @param methodName
	 *            方法名
	 * @param param
	 *            参数
	 * @return
	 */
	public static Object call(String endpoint, Class<?> interfaceClass, String methodName, Object... param) {
		// 得到远程接口
		Object interfaceInstance = getInterface(endpoint, interfaceClass);

		Object result = ClassUtil.invokeMethodForce(interfaceInstance, methodName, param);
		return result;
	}

	/**
	 * 根据endpoint得到WebService远程的调用接口
	 * 
	 * @param endpoint
	 *            调用地址
	 * @param interfaceClass
	 *            webservice的接口
	 * @return WebService的远程调用接口，调用该访问等同于调用远程方法
	 */
	public static <T> T getInterface(String endpoint, Class<T> interfaceClass) {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setServiceClass(interfaceClass);
		factory.setAddress(endpoint);
		T instance = interfaceClass.cast(factory.create());
		return (T) instance;
	}

	/**
	 * 动态调用webservice方法，不需要知道接口，只需要知道endpoint，方法名，参数就可以得到返回值
	 * 
	 * @param wsdlAddress
	 *            wsdl的路径，必须在http或https中可以直接访问
	 * @param methodName
	 *            调用的服务名（对应Facade或WebService接口的方法名）
	 * @param param
	 *            参数,可变参数
	 * @return 返回值为对象数组
	 * @throws Exception
	 */
	public static Object[] dynamicCallReturnObjArr(String wsdlAddress, String methodName, Object... param)
			throws Exception {
		return dynamicCallReturnObjArr(wsdlAddress,null,methodName,param);
	}
	
	/**
	 * 创建Client
	 * 
	 * @param wsdlAddress wsdl地址
	 * @param qname QName
	 * @return
	 */
	public static Client createClient(String wsdlAddress,QName qname){
		return createClient(wsdlAddress, qname, null, null);
	}

	/**
	 * 创建Client
	 * 
	 * @param wsdlAddress wsdl地址
	 * @param qname QName
	 * @param connectionTimeout 连接超时时间,单位 为毫秒。为空时使用默认值 
	 * @param receiveTimeout 请求超时时间,单位 为毫秒。为空时使用默认值 
	 * @return
	 */
	public static Client createClient(String wsdlAddress,Long connectionTimeout,Long receiveTimeout){
		return createClient(wsdlAddress,null,connectionTimeout,receiveTimeout);
	}
	
	/**
	 * 创建Client
	 * 
	 * @param wsdlAddress wsdl地址
	 * @param qname QName
	 * @param connectionTimeout 连接超时时间,单位 为毫秒。为空时使用默认值 
	 * @param receiveTimeout 请求超时时间,单位 为毫秒。为空时使用默认值 
	 * @return
	 */
	public static Client createClient(String wsdlAddress,QName qname,Long connectionTimeout,Long receiveTimeout){
		Assert.notNull(wsdlAddress, "wsdl不能为空!");
		
		JaxWsDynamicClientFactory dynamicClient = JaxWsDynamicClientFactory.newInstance();
		Client client = null;

		// 判断是否需要 qname
		if(qname == null){
			client = dynamicClient.createClient(wsdlAddress);
		} else {
			client = dynamicClient.createClient(wsdlAddress, qname);
		}
		
		if(connectionTimeout != null || receiveTimeout != null){
			Conduit conduit = client.getConduit();
			if(conduit instanceof HTTPConduit){
				HTTPConduit con = (HTTPConduit) conduit;
				HTTPClientPolicy policy = new HTTPClientPolicy();
				if(connectionTimeout != null){
					policy.setConnectionTimeout(connectionTimeout); //连接超时时间
				}
				
				if(receiveTimeout != null){
					policy.setReceiveTimeout(receiveTimeout);//请求超时时间.
				}
				con.setClient(policy);
			}
		}

		return client;
	}

	/**
	 * 动态调用webservice方法，不需要知道接口，只需要知道endpoint，方法名，参数就可以得到返回值
	 * 
	 * @param wsdlAddress
	 *            wsdl的路径，必须在http或https中可以直接访问
	 * @param qname
	 *            web service对应的QName
	 * @param methodName
	 *            调用的服务名（对应Facade或WebService接口的方法名）
	 * @param param
	 *            参数,可变参数
	 * @return 返回值为对象数组
	 * @throws Exception
	 */
	public static Object[] dynamicCallReturnObjArr(String wsdlAddress, QName qname, String methodName, Object... param)
			throws Exception {
		Client client = createClient(wsdlAddress, qname);

		Object[] rspArr = client.invoke(methodName, param);

		return rspArr;
	}
	
	/**
	 * 设置 client 的
	 * @param client
	 * @param connectionTimeout
	 * @param receiveTimeout
	 * @return
	 */
	public static Client setConnectTimeOut(Client client,long connectionTimeout,long receiveTimeout){
		Conduit conduit = client.getConduit();
		if(conduit instanceof HTTPConduit){
			HTTPConduit con = (HTTPConduit) conduit;
			HTTPClientPolicy policy = new HTTPClientPolicy();
			policy.setConnectionTimeout(connectionTimeout); //连接超时时间
			policy.setReceiveTimeout(receiveTimeout);//请求超时时间.
			con.setClient(policy);
		}
		
		return client;
	}
	
	/**
	 * 动态调用webservice方法，不需要知道接口，只需要知道endpoint，方法名，参数就可以得到返回值
	 * 
	 * @param wsdlAddress
	 *            wsdl的路径，必须在http或https中可以直接访问
	 * @param qname
	 *            web service对应的QName
	 * @param methodName
	 *            调用的服务名（对应Facade或WebService接口的方法名）
	 * @param param
	 *            参数,可变参数
	 * @return 返回值为对象数组
	 * @throws Exception
	 */
	public static Object[] dynamicCallReturnObjArr(Client client, String methodName, Object... param)
			throws Exception {
		if(log.isDebugEnabled()){
			log.debug("调用"+param);
		}
		Object[] rspArr = client.invoke(methodName, param);

		if(log.isDebugEnabled()){
			log.debug("调用" + rspArr);
		}

		return rspArr;
	}

	/**
	 * 动态调用webservice方法，不需要知道接口，只需要知道endpoint，方法名，参数就可以得到返回值
	 * 
	 * @param wsdlAddress
	 *            wsdl的路径，必须在http或https中可以直接访问
	 * @param methodName
	 *            调用的服务名（对应Facade或WebService接口的方法名）
	 * @param param
	 *            参数,可变参数
	 * @return 返回值为对象
	 * @throws Exception
	 */
	public static Object dynamicCall(String wsdlAddress, String methodName, Object... param) throws Exception {
		return dynamicCall(wsdlAddress,null,methodName,param);
	}

	/**
	 * 动态调用webservice方法，不需要知道接口，只需要知道endpoint，方法名，参数就可以得到返回值
	 * 
	 * @param wsdlAddress
	 *            wsdl的路径，必须在http或https中可以直接访问
	 * @param qname
	 *            web service对应的QName
	 * @param methodName
	 *            调用的服务名（对应Facade或WebService接口的方法名）
	 * @param param
	 *            参数,可变参数
	 * @return 返回值为对象
	 * @throws Exception
	 */
	public static Object dynamicCall(String wsdlAddress, QName qname , String methodName, Object... param) throws Exception {
		Object[] res = dynamicCallReturnObjArr(wsdlAddress, qname, methodName, param);
		if (CollectionUtils.notEmpty(res)) {
			return res[0];
		}
		return null;
	}

	/**
	 * 动态调用webservice方法，不需要知道接口，只需要知道endpoint，方法名，参数就可以得到返回值
	 * 
	 * @param wsdlAddress
	 *            wsdl的路径，必须在http或https中可以直接访问
	 * @param qname
	 *            web service对应的QName
	 * @param methodName
	 *            调用的服务名（对应Facade或WebService接口的方法名）
	 * @param param
	 *            参数,可变参数
	 * @return 返回值为对象
	 * @throws Exception
	 */
	public static Object dynamicCall(Client client,String methodName, Object... param) throws Exception {
		Object[] res = dynamicCallReturnObjArr(client,methodName, param);
		if (CollectionUtils.notEmpty(res)) {
			return res[0];
		}
		return null;
	}

	public static void main(String[] args) throws Exception {
//		 String addr = "http://localhost:8080/ws/TestWebService?wsdl";
//		 Object result = dynamicCall(addr, "sayHello","ronald");
//		 System.out.println(result);
//
//		 Object result1 = dynamicCall(addr, "sayHelloB");
//		 System.out.println(result1);
		 
		 System.out.println(dynamicCall("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl", "getMobileCodeInfo", "13510254421",""));
//		 System.out.println(dynamicCall("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl", "getDatabaseInfo",""));
		 
		 
//		 TestWebService cls = WebServiceClientUtil.getInterface("http://localhost:8080/ws/TestWebService",TestWebService.class);
//		 System.out.println(cls.sayHello("ronald2"));
//		 System.out.println(cls.sayHelloB());

	}
	
}
